glibc2.43 新版本 tcache 相关源码分析
glibc2.43 新版本 tcache 相关源码分析
tcache_perthread_struct 定义
1 | // glibc2.43: malloc.c line 2910 |
entries[i] :第 i 个 tcache bin 的链表头
num_slots[i] : 这个 bin 还能放多少个 chunk ,这里与 glibc 2.39 存在差异,由 counts[] 变成了 num_slots[]
tcache_put_n 定义
这段 tcache_put_n 是 glibc 2.43 把一个 free chunk 放进当前线程 tcache 的核心代码。
1 | // glibc2.43: malloc.c line 3015 |
这里可以看到 tcache 的逻辑发生了完全相反的改变,--(tcache->num_slots[tc_idx]) 表示当前还剩多少槽位,以往的 glibc2.39 版本是 ++(tcache->counts[tc_idx]) 记录已有多少 chunk 。
其中 # define TCACHE_FILL_COUNT 16 默认定义了 num_slots[tc_idx] 最大值是 16 ,也就是说初始 bin,num_slots[i] = 16 ,每放进一个 chunk : num_slots[i]-- ,每取出一个 chunk :num_slots[i]++ 。
tcache_entry 定义
1 | // glibc2.43 : malloc.c line 2897 |
key 用来做 double free 检查 ,值得一提的是 ,glibc2.43 对 double free 的检查更加严格,相关代码如下:
调用点:
1 | // glibc2.43 malloc.c line 3365 |
在 __libc_free 里,glibc 先把要释放的 chunk 用户区解释成 tcache_entry :tcache_entry *e = (tcache_entry *) chunk2mem (p); ,然后做一个简单的怀疑判断:if (e->key == tcache_key) :
- 如果 e->key != tcache_key:大概率没在 tcache 里,继续正常 free
- 如果 e->key == tcache_key:怀疑这个 chunk 已经进过 tcache,于是进入复核函数
tcache_double_free_verify 定义
1 | // glibc2.43: malloc.c line 3161 |
通过阅读源码可以看出,复合函数有三个检查,分别是检查链表长度、检查 chunk 用户区地址对齐 、检查释放的 chunk 的用户区地址是否已经在 tcache 链表里 。
在 glibc2.39 里 ,__libc_free 直接内联做这段检查,而 glibc2.43 把它抽成了单独函数,且更加严格,不仅仅只查对应 size 的 bin ,而是查当前线程整个 tcache 表。
large tcache bins
在 glibc 2.43 内额外预留了 12 个给大 chunk 用的 tcache bin
1 |
但是默认设置还是:
1 | .tcache_max_bytes = MAX_TCACHE_SMALL_SIZE + 1 |
也就是只到 0x410 左右,所以默认行为和以前差不多。
只有当你把 tcache_max_bytes 调大后,才会用到这些新增的 large bins。
tcache_perthread_struct 创建
在 glibc2.43 里,tcache_perthread_struct 不是程序一启动就立刻创建,而是按需延迟创建,线程刚开始时:
1 | static __thread tcache_perthread_struct *tcache = |
也就是先指向一个 dummy 的 inactive 状态,不是真实分配的结构体。
而真正的创建发生在调用 tcache_init() 的时候:
1 | // glibc2.43 malloc.c line 3224 |
什么时候会触发 tcache_init 呢?
这里主要有两种情况:
- 第一次需要
tcache的malloc/free路径
当glibc发现当前线程的tcache还是inactive,但这次操作想使用tcache,就会去初始化。 - 第一次
free想把chunk放进tcache时
1 | if (__glibc_unlikely (tcache_inactive ())) |
然后:
1 | static void __attribute_noinline__ |
先初始化真实的 tcache_perthread_struct ,然后重新执行一次 free 。
也就是说只有当前线程第一次真正需要用到 tcache 时,glibc 才会动态分配一个堆块,拿来作为这个线程的 tcache_perthread_struct。
- 标题: glibc2.43 新版本 tcache 相关源码分析
- 作者: SpaceDraG0n
- 创建于 : 2026-05-07 00:00:00
- 更新于 : 2026-05-07 00:00:00
- 链接: https://spacedrag0n-1.github.io/posts/glibc2.43 新版本 tcache 相关源码分析/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。